﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;
using LayerTableRecord = Autodesk.AutoCAD.DatabaseServices.LayerTableRecord;

namespace ChangDiTools
{
    internal static class Tools
    {
        /// <summary>
        /// 添加实体。
        /// 将实体添加到当前模型空间上。
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns>ObjectId</returns>
        public static ObjectId AddEntity(this Database db, Entity entity)
        {
            ObjectId id;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                using (BlockTableRecord btr = (BlockTableRecord)trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite))
                {
                    id = btr.AppendEntity(entity);
                    trans.AddNewlyCreatedDBObject(entity, true);
                }

                trans.Commit();
            }

            return id;
        }

        //db.AddEntity(ents)添加多个对象
        public static ObjectIdCollection AddEntity(this Database db, params Entity[] ents)
        {
            ObjectIdCollection ids = new ObjectIdCollection();
            for (int i = 0; i < ents.Length; i++)
            {
                ids.Add(db.AddEntity(ents[i]));
            }

            return ids;
        }

        /// <summary>
        /// 用极坐标方法获取点
        /// </summary>
        /// <param name="p">基点</param>
        /// <param name="dAng">角度</param>
        /// <param name="dDist">距离</param>
        /// <returns></returns>
        public static Point2d PolarPoints(Point2d p, double dAng, double dDist)
        {
            return new Point2d(p.X + dDist * Math.Cos(dAng)
                , p.Y + dDist * Math.Sin(dAng));
        }

        public static Point3d PolarPoints(Point3d p, double dAng, double dDis)
        {
            return new Point3d(p.X + dDis * Math.Cos(dAng),
                p.Y + dDis * Math.Sin(dAng),
                p.Z);
        }
        /// <summary>
        /// 获取直线中点
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public static Point3d MidPoints(Line line)
        {
            return MidPoints(line.StartPoint, line.EndPoint);
        }

        public static Point3d MidPoints(Point3d startPoint3D, Point3d endPoint3D)
        {
            return new Point3d((startPoint3D.X + endPoint3D.X) / 2,
                (startPoint3D.Y + endPoint3D.Y) / 2,
                (startPoint3D.Z + endPoint3D.Z) / 2);
        }

        public static void ReverseLine(this Line line)
        {
            if (line.StartPoint.X > line.EndPoint.X)
            {
                line.ReverseCurve();
            }
        }




        /// <summary>
        /// 根据两点绘制网格，图层颜、线型色仍需要完善
        /// </summary>
        public static void CreateGridLine()
        {
            string layername = "ZC-LLT-SG";
            AddLayer(layername);
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;
            PromptPointOptions ppo = new PromptPointOptions("")
            {
                Message = "\n选择第一个点"
            };
            PromptPointResult ppr = ed.GetPoint(ppo);
            Point3d p1 = ppr.Value;
            if (ppr.Status == PromptStatus.Cancel)
            {
                return;
            }

            ppo.Message = "\n选择第二个点";
            ppo.UseBasePoint = true;
            ppo.BasePoint = p1;
            ppr = ed.GetPoint(ppo);
            Point3d p2 = ppr.Value;
            if (ppr.Status == PromptStatus.Cancel)
            {
                return;
            }

            double x1 = Math.Floor(p1.X < p2.X ? p1.X : p2.X);
            double x2 = Math.Ceiling(p1.X < p2.X ? p2.X : p1.X);
            double y1 = Math.Floor(p1.Y < p2.Y ? p1.Y : p2.Y);
            double y2 = Math.Ceiling(p1.Y < p2.Y ? p2.Y : p1.Y);
            int w = 3; //左右留白
            int h = 2; //上下留白
            int c1 = 7; //主栅格颜色
            int c2 = 8; //次栅格颜色
            for (int i = -w + 1; i < x2 - x1 + w; i++)
            {
                Point3d st = new Point3d(x1 + i, y1 - h, 0);
                Point3d end = new Point3d(x1 + i, y2 + h, 0);
                using (Line l = new Line(st, end))
                {
                    l.ColorIndex = (i + w) % 5 == 0 ? c1 : c2;
                    l.Layer = layername;
                    db.AddEntity(l);
                }
            }

            for (int j = -h + 1; j < y2 - y1 + h; j++)
            {
                Point3d st = new Point3d(x1 - w, y1 + j, 0);
                Point3d end = new Point3d(x2 + w, y1 + j, 0);
                using (Line l = new Line(st, end))
                {
                    l.ColorIndex = (j + h) % 5 == 0 ? c1 : c2;
                    //l.Linetype = "CENTER";
                    l.Layer = layername;
                    db.AddEntity(l);
                }
            }

            Point2dCollection pts = new Point2dCollection {
                new Point2d(x1 - w, y2 + h),
                new Point2d(x2 + w, y2 + h),
                new Point2d(x2 + w, y1 - h),
                new Point2d(x1 - w, y1 - h)
            };
            using (Polyline rect = new Polyline())
            {
                foreach (Point2d pt in pts)
                {
                    rect.AddVertexAt(0, pt, 0, 0, 0);
                }

                rect.Closed = true;
                rect.ConstantWidth = 0.1;
                rect.Layer = layername;
                db.AddEntity(rect);
            }

            //doc.SendStringToExecute("._zoom _e ", true, false, false);//注意空格
        }

        //批量创建图层
        public static void AddLayer(string layerName, short c = 0)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForWrite) as LayerTable;
                if (!lt.Has(layerName))
                {
                    LayerTableRecord ltr = new LayerTableRecord
                    {
                        Name = layerName
                    };
                    ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, c);
                    lt.Add(ltr);
                    tr.AddNewlyCreatedDBObject(ltr, true);
                }

                tr.Commit();
            }
        }

        /// <summary>
        ///道路坡度计算
        ///没有进行图层设置
        ///只能通过标高及坡长计算坡度，需要手动设置标高及坡长为正确值
        ///先选大数，再选小数，否则坡度为负
        ///按Esc退出时会进行视图缩放，原因不明
        /// </summary>
        public static void RoadSlope()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            string[] strs = new string[4];
            DBText[] dbTexts = new DBText[4];
            do
            {
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    SelectionFilter filter = new SelectionFilter(new[] { new TypedValue((int)DxfCode.Start, "TEXT") });
                    try
                    {
                        PromptSelectionOptions pso = new PromptSelectionOptions
                        {
                            MessageForAdding = "\n请选择起点标高(ESC退出)",
                            SingleOnly = true
                        };
                        PromptSelectionResult pr = ed.GetSelection(pso, filter);
                        if (pr.Status == PromptStatus.OK)
                        {
                            ObjectId[] ids = pr.Value.GetObjectIds();
                            dbTexts[0] = tr.GetObject(ids[0], OpenMode.ForWrite) as DBText;
                        }
                        else if (pr.Status == PromptStatus.Cancel)
                        {
                            return;
                        }

                        pso.MessageForAdding = "\n请选择终点标高(ESC退出)";
                        pr = ed.GetSelection(pso, filter);
                        if (pr.Status == PromptStatus.OK)
                        {
                            ObjectId[] ids = pr.Value.GetObjectIds();
                            dbTexts[1] = tr.GetObject(ids[0], OpenMode.ForWrite) as DBText;
                        }
                        else if (pr.Status == PromptStatus.Cancel)
                        {
                            return;
                        }

                        pso.MessageForAdding = "\n请选择坡长(ESC退出)";
                        pr = ed.GetSelection(pso, filter);
                        if (pr.Status == PromptStatus.OK)
                        {
                            ObjectId[] ids = pr.Value.GetObjectIds();
                            dbTexts[2] = tr.GetObject(ids[0], OpenMode.ForWrite) as DBText;
                        }
                        else if (pr.Status == PromptStatus.Cancel)
                        {
                            return;
                        }

                        pso.MessageForAdding = "\n请选择坡度(ESC退出)";
                        pr = ed.GetSelection(pso, filter);
                        if (pr.Status == PromptStatus.OK)
                        {
                            ObjectId[] ids = pr.Value.GetObjectIds();
                            dbTexts[3] = tr.GetObject(ids[0], OpenMode.ForWrite) as DBText;
                        }
                        else if (pr.Status == PromptStatus.Cancel)
                        {
                            return;
                        }

                        double[] d = new double[4];
                        for (int i = 0; i < d.Length - 1; i++)
                        {
                            d[i] = double.Parse(dbTexts[i].TextString);
                        }

                        double slope = (d[0] - d[1]) / d[2];
                        strs[3] = slope.ToString("p").Replace(" ", "");
                        dbTexts[3].TextString = strs[3];
                    }
                    catch (Autodesk.AutoCAD.Runtime.Exception e)
                    {
                        ed.WriteMessage(e.Message);
                    }

                    tr.Commit();
                }
            } while (true);

        }

        /// <summary>
        /// 坐标标注
        /// 未解决问题：
        /// 1.捕捉（只打开端点和交点）
        /// 2.设置图层
        /// 3.文字样式
        /// 4.旋转角度
        /// 5.不同文字样式下水平线的宽度
        /// 6.不同坐标系如何设置？如何在用户坐标系标注世界坐标系？
        /// 7.X-Y坐标值：181116已修复
        /// </summary>
        /// <param name="accuracy">设置精度，默认为3为</param>
        /// <param name="tHeight">设置文字高度，默认为3为</param>
        public static void CoordinateCallOut(string accuracy = "f3", double tHeight = 3)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            do
            {
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    double length = 0;
                    Point3d[] pts = new Point3d[5];
                    string sX = "X=";
                    string sY = "Y=";
                    PromptPointOptions ppo = new PromptPointOptions("\n拾取标注点(ESC退出)")
                    {
                        AllowNone = false
                    };
                    PromptPointResult ppr = ed.GetPoint(ppo);
                    if (ppr.Status == PromptStatus.OK)
                    {
                        pts[0] = ppr.Value;
                    }
                    else
                    {
                        return;
                    }

                    ppo.Message = "\n拾取下一点";
                    ppo.AllowNone = false;
                    ppo.UseBasePoint = true;
                    ppo.BasePoint = pts[0];
                    ppr = ed.GetPoint(ppo);
                    if (ppr.Status == PromptStatus.OK)
                    {
                        pts[1] = ppr.Value;
                    }
                    else
                    {
                        return;
                    }

                    Line l1 = new Line(pts[0], pts[1]);
                    //ObjectId id = l1.ObjectId;
                    sX += l1.StartPoint.Y.ToString(accuracy); //测量坐标与数学坐标XY互换
                    sY += l1.StartPoint.X.ToString(accuracy);

                    DBText textX = new DBText
                    {
                        Height = tHeight,
                        TextString = sX
                    };

                    DBText textY = new DBText
                    {
                        Height = tHeight,
                        TextString = sY
                    };


                    if (pts[1].X >= pts[0].X)
                    {
                        textX.HorizontalMode = TextHorizontalMode.TextLeft;
                        pts[4] = new Point3d(pts[1].X + textX.Height * 0.15, pts[1].Y, 0); //x定位点
                        textY.HorizontalMode = TextHorizontalMode.TextLeft;
                        pts[3] = new Point3d(pts[1].X + textY.Height * 0.15, pts[1].Y - textY.Height * 0.18233,
                            0); //Y定位点

                        //确定水平直线长度
                        length = sX.Length > sY.Length
                            ? sX.Length * textX.WidthFactor * textX.Height
                            : sY.Length * textY.WidthFactor * textY.Height;
                    }
                    else
                    {
                        textX.HorizontalMode = TextHorizontalMode.TextRight;
                        pts[4] = new Point3d(pts[1].X - textX.Height * 0.08, pts[1].Y, 0); //x定位点
                        textY.HorizontalMode = TextHorizontalMode.TextRight;
                        pts[3] = new Point3d(pts[1].X - textY.Height * 0.08, pts[1].Y - textY.Height * 0.2, 0); //Y定位点

                        //确定水平直线长度
                        length = sX.Length > sY.Length
                            ? sX.Length * textX.WidthFactor * textX.Height
                            : sY.Length * textY.WidthFactor * textY.Height;
                        length = 0 - length;

                    }

                    textX.Position = pts[4];
                    textX.VerticalMode = TextVerticalMode.TextBottom;
                    textX.AlignmentPoint = pts[4];

                    textY.Position = pts[3];
                    textY.VerticalMode = TextVerticalMode.TextTop;
                    textY.AlignmentPoint = pts[3];


                    pts[2] = new Point3d(pts[1].X + length * 0.75, pts[1].Y, 0);

                    Line l2 = new Line(pts[1], pts[2]);
                    DBObjectCollection objs = new DBObjectCollection {
                        l1,
                        l2,
                        textX,
                        textY
                    };
                    foreach (Entity entity in objs)
                    {
                        btr.AppendEntity(entity);
                        tr.AddNewlyCreatedDBObject(entity, true);
                    }

                    tr.Commit();
                }
            } while (true);
        }

        public static void CoordinateCallOut1(int accuracy = 3, double tHeight = 3)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            do
            {
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    double length = 0;
                    Point3d[] pts = new Point3d[5];
                    string sX = "X=";
                    string sY = "Y=";
                    PromptPointOptions ppo = new PromptPointOptions("\n拾取标注点(ESC退出)")
                    {
                        AllowNone = false
                    };
                    PromptPointResult ppr = ed.GetPoint(ppo);
                    if (ppr.Status == PromptStatus.OK)
                    {
                        pts[0] = ppr.Value;
                    }
                    else
                    {
                        return;
                    }

                    ppo.Message = "\n拾取下一点";
                    ppo.AllowNone = false;
                    ppo.UseBasePoint = true;
                    ppo.BasePoint = pts[0];
                    ppr = ed.GetPoint(ppo);
                    if (ppr.Status == PromptStatus.OK)
                    {
                        pts[1] = ppr.Value;
                    }
                    else
                    {
                        return;
                    }

                    Line l1;
                    using (Transaction tr2 = db.TransactionManager.StartTransaction())
                    {
                        l1 = new Line(pts[0], pts[1]);
                        btr.AppendEntity(l1);
                        tr.AddNewlyCreatedDBObject(l1, true);
                        tr2.Commit();
                    }

                    //必须将对象添加到数据库才能有ObjectID
                    string sX1 = sX + l1.StartPoint.Y.ToString("f" + accuracy); //用来确定直线长度
                    string sY1 = sY + l1.StartPoint.X.ToString("f" + accuracy);
                    string id = l1.ObjectId.ToString().Trim(new char[] { '(', ')' });
                    sX += $"%<\\AcObjProp Object(%<\\_ObjId {id}>%).StartPoint \\f \"%lu2%pt2%pr{accuracy}\">%"; //测量坐标与数学坐标XY互换
                    sY += $"%<\\AcObjProp Object(%<\\_ObjId {id}>%).StartPoint \\f \"%lu2%pt1%pr{accuracy}\">%";

                    DBText textX = new DBText
                    {
                        Height = tHeight,
                        TextString = sX
                    };

                    DBText textY = new DBText
                    {
                        Height = tHeight,
                        TextString = sY
                    };


                    if (pts[1].X >= pts[0].X)
                    {
                        textX.HorizontalMode = TextHorizontalMode.TextLeft;
                        pts[4] = new Point3d(pts[1].X + textX.Height * 0.15, pts[1].Y, 0); //x定位点
                        textY.HorizontalMode = TextHorizontalMode.TextLeft;
                        pts[3] = new Point3d(pts[1].X + textY.Height * 0.15, pts[1].Y - textY.Height * 0.18233,
                            0); //Y定位点

                        //确定水平直线长度
                        length = sX1.Length > sY1.Length
                            ? sX1.Length * textX.WidthFactor * textX.Height
                            : sY1.Length * textY.WidthFactor * textY.Height;
                    }
                    else
                    {
                        textX.HorizontalMode = TextHorizontalMode.TextRight;
                        pts[4] = new Point3d(pts[1].X - textX.Height * 0.08, pts[1].Y, 0); //x定位点
                        textY.HorizontalMode = TextHorizontalMode.TextRight;
                        pts[3] = new Point3d(pts[1].X - textY.Height * 0.08, pts[1].Y - textY.Height * 0.2, 0); //Y定位点

                        //确定水平直线长度
                        length = sX1.Length > sY1.Length
                            ? sX1.Length * textX.WidthFactor * textX.Height
                            : sY1.Length * textY.WidthFactor * textY.Height;
                        length = 0 - length;

                    }

                    textX.Position = pts[4];
                    textX.VerticalMode = TextVerticalMode.TextBottom;
                    textX.AlignmentPoint = pts[4];

                    textY.Position = pts[3];
                    textY.VerticalMode = TextVerticalMode.TextTop;
                    textY.AlignmentPoint = pts[3];


                    pts[2] = new Point3d(pts[1].X + length * 0.75, pts[1].Y, 0);

                    Line l2 = new Line(pts[1], pts[2]);
                    DBObjectCollection objs = new DBObjectCollection {
                        l2,
                        textX,
                        textY
                    };
                    foreach (Entity entity in objs)
                    {
                        btr.AppendEntity(entity);
                        tr.AddNewlyCreatedDBObject(entity, true);
                    }

                    tr.Commit();
                    ed.Regen(); //重生成，避免显示####
                }
            } while (true);
        }

        /// <summary>
        /// 标注多段线坐标
        /// </summary>
        /// <param name="accuracy"></param>
        /// <param name="tHeight"></param>
        public static void GetPolylineCoordinat(string accuracy = "f3", double tHeight = 3)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            //选择多段线对象，并获取点的集合
            PromptEntityOptions peo = new PromptEntityOptions("\n选择要标注坐标的多段线");
            peo.SetRejectMessage("\n只能选择多段线（Polyline）");
            peo.AddAllowedClass(typeof(Polyline), false);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
            {
                return;
            }

            Polyline pl = new Polyline();
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                pl = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Polyline;
                double length = 0;
                if (pl != null)
                {
                    int vn = pl.NumberOfVertices;
                    for (int i = 0; i < vn; i++)
                    {

                        using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                        {
                            Point3d pt = pl.GetPoint3dAt(i);

                            Point3d[] pts = new Point3d[5];
                            string sX = "X=";
                            string sY = "Y=";
                            pts[0] = pl.GetPoint3dAt(i);

                            PromptPointOptions ppo = new PromptPointOptions("")
                            {
                                Message = "\n拾取下一点",
                                AllowNone = false,
                                UseBasePoint = true,
                                BasePoint = pts[0]
                            };
                            PromptPointResult ppr = ed.GetPoint(ppo);
                            if (ppr.Status == PromptStatus.OK)
                            {
                                pts[1] = ppr.Value;
                            }
                            else
                            {
                                return;
                            }

                            BlockTableRecord btr = tr2.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                            Line l1 = new Line(pts[0], pts[1]);
                            //ObjectId id = l1.ObjectId;
                            sX += l1.StartPoint.Y.ToString(accuracy); //测量坐标与数学坐标XY互换
                            sY += l1.StartPoint.X.ToString(accuracy);

                            DBText textX = new DBText
                            {
                                Height = tHeight,
                                TextString = sX
                            };

                            DBText textY = new DBText
                            {
                                Height = tHeight,
                                TextString = sY
                            };


                            if (pts[1].X >= pts[0].X)
                            {
                                textX.HorizontalMode = TextHorizontalMode.TextLeft;
                                pts[4] = new Point3d(pts[1].X + textX.Height * 0.15, pts[1].Y, 0); //x定位点
                                textY.HorizontalMode = TextHorizontalMode.TextLeft;
                                pts[3] = new Point3d(pts[1].X + textY.Height * 0.15, pts[1].Y - textY.Height * 0.18233,
                                    0); //Y定位点

                                //确定水平直线长度
                                length = sX.Length > sY.Length
                                    ? sX.Length * textX.WidthFactor * textX.Height
                                    : sY.Length * textY.WidthFactor * textY.Height;
                            }
                            else
                            {
                                textX.HorizontalMode = TextHorizontalMode.TextRight;
                                pts[4] = new Point3d(pts[1].X - textX.Height * 0.08, pts[1].Y, 0); //x定位点
                                textY.HorizontalMode = TextHorizontalMode.TextRight;
                                pts[3] = new Point3d(pts[1].X - textY.Height * 0.08, pts[1].Y - textY.Height * 0.2,
                                    0); //Y定位点

                                //确定水平直线长度
                                length = sX.Length > sY.Length
                                    ? sX.Length * textX.WidthFactor * textX.Height
                                    : sY.Length * textY.WidthFactor * textY.Height;
                                length = 0 - length;

                            }

                            textX.Position = pts[4];
                            textX.VerticalMode = TextVerticalMode.TextBottom;
                            textX.AlignmentPoint = pts[4];

                            textY.Position = pts[3];
                            textY.VerticalMode = TextVerticalMode.TextTop;
                            textY.AlignmentPoint = pts[3];


                            pts[2] = new Point3d(pts[1].X + length * 0.75, pts[1].Y, 0);

                            Line l2 = new Line(pts[1], pts[2]);
                            DBObjectCollection objs = new DBObjectCollection {
                                l1,
                                l2,
                                textX,
                                textY
                            };
                            foreach (Entity entity in objs)
                            {
                                btr.AppendEntity(entity);
                                tr2.AddNewlyCreatedDBObject(entity, true);
                            }

                            tr2.Commit();
                        }
                    }
                }

                tr.Commit();
            }
        }

        public static void GetEle(string accuracy = "f3", double tHeight = 0.6)
        {
            string layerName = "ZC-LJ-BPD";
            double baseElv = GetEleBase(layerName, accuracy, tHeight);
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            Point3d[] pts = new Point3d[3];

            do
            {
                PromptPointOptions ppo = new PromptPointOptions("")
                {
                    AllowNone = false,
                    //选择要标注的点
                    Message = "\n拾取要标注的点"
                };
                PromptPointResult ppr = ed.GetPoint(ppo);
                if (ppr.Status == PromptStatus.OK)
                {
                    pts[1] = ppr.Value;
                }
                else
                {
                    return;
                }

                //插入图块，标注标高，设置精度
                pts[2] = new Point3d(pts[1].X, pts[1].Y + tHeight * 1.5, 0); //文本插入点
                string ele = (pts[1].Y - baseElv).ToString(accuracy);

                using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                {
                    BlockTableRecord btr1 = tr2.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    //var lt2 = tr2.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
                    DBText textEle = new DBText()
                    {
                        Height = tHeight,
                        TextString = ele,
                        VerticalMode = TextVerticalMode.TextVerticalMid,
                        HorizontalMode = TextHorizontalMode.TextMid,
                        AlignmentPoint = pts[2]
                    };
                    textEle.Layer = layerName;
                    db.AddEntity(textEle);
                    tr2.Commit();
                }

                //插入图块
                double s = tHeight / 3;
                Scale3d scale3D = new Scale3d(s, s, s); //缩放比例
                db.InsertBlockTableRecord(layerName, layerName, pts[1], scale3D, 0);
            } while (true);
        }

        public static void GetPolylineEle(string accuracy = "f3", double tHeight = 0.6)
        {
            string layerName = "ZC-LJ-BPD";
            double baseElv = GetEleBase(layerName, accuracy, tHeight);
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            Point3d[] pts = new Point3d[2];

            //选择多段线对象，并获取点的集合
            PromptEntityOptions peo = new PromptEntityOptions("\n选择要标注坐标的多段线");
            peo.SetRejectMessage("\n只能选择多段线（Polyline）");
            peo.AddAllowedClass(typeof(Polyline), false);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
            {
                return;
            }

            Polyline pl = new Polyline();
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                pl = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Polyline;
                if (pl != null)
                {
                    int vn = pl.NumberOfVertices;
                    for (int i = 0; i < vn; i++)
                    {

                        using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                        {
                            pts[0] = pl.GetPoint3dAt(i);
                            pts[1] = new Point3d(pts[0].X, pts[0].Y + tHeight * 1.5, 0); //文本插入点
                            string ele = (pts[0].Y - baseElv).ToString(accuracy);

                            using (OpenCloseTransaction tr3 = db.TransactionManager.StartOpenCloseTransaction())
                            {
                                BlockTableRecord btr1 = tr3.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                                //var lt2 = tr2.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
                                DBText textEle = new DBText()
                                {
                                    Height = tHeight,
                                    TextString = ele,
                                    VerticalMode = TextVerticalMode.TextVerticalMid,
                                    HorizontalMode = TextHorizontalMode.TextMid,
                                    AlignmentPoint = pts[1]
                                };
                                textEle.Layer = layerName;
                                db.AddEntity(textEle);
                                tr3.Commit();
                            }

                            //插入图块
                            double s = tHeight / 3;
                            Scale3d scale3D = new Scale3d(s, s, s); //缩放比例
                            db.InsertBlockTableRecord(layerName, layerName, pts[0], scale3D, 0);

                            tr2.Commit();
                        }
                    }
                }

                tr.Commit();
            }
        }

        /// <summary>
        /// 插入一个块参照到CAD图形中
        /// </summary>
        /// <param name="spaceId">模型空间Id</param>
        /// <param name="layer">要插入的图层名</param>
        /// <param name="blockName">块名</param>
        /// <param name="postion">插入点</param>
        /// <param name="scale">缩放比例</param>
        /// <param name="rotateAngle">旋转角度</param>
        /// <returns></returns>
        public static ObjectId InsertBlockTableRecord(this Database db, string layer, string blockName,
        Point3d postion, Scale3d scale, double rotateAngle)
        {
            ObjectId blockRefId; //存储要插入的块参照的Id
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                if (!bt.Has(blockName))
                {
                    return ObjectId.Null; //如果没有blockName的块，程序返回
                }
                //以写的方式打开空间
                BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                //创建块参照的插入点
                BlockReference br = new BlockReference(postion, bt[blockName])
                {
                    ScaleFactors = scale,
                    Layer = layer,
                    Rotation = rotateAngle
                };
                blockRefId = db.AddEntity(br);
                tr.Commit();
            }

            return blockRefId;
        }

        /// <summary>
        /// 标高标注的基本方法
        /// </summary>
        /// <param name="layerName">图层名</param>
        /// <param name="accuracy">精度</param>
        /// <param name="tHeight">文字高度</param>
        /// <returns>基点标高</returns>
        public static double GetEleBase(string layerName, string accuracy = "f3", double tHeight = 0.6)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            double baseElv = -100000.0;
            AddLayer(layerName); //添加图层

            //var osm = Application.GetSystemVariable("OSMODE"); //获取当前捕捉设置
            //Application.SetSystemVariable("OSMODE", 0); //清空捕捉

            //判断标高块是否存在
            //using (var tr = db.TransactionManager.StartOpenCloseTransaction()) {
            //    var bt = tr.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
            //    if (bt.Has(layerName) == false)
            //    {
            //        var ids=AddBlock(layerName);
            //        //新建块
            //        BlockTableRecord btr = new BlockTableRecord();
            //        btr.Name = layerName;
            //        //btr.AppendEntity(acPoly);
            //        foreach (ObjectId id in ids)
            //        {
            //            btr.AppendEntity(id.GetObject(OpenMode.ForWrite) as Entity);
            //        }
            //        bt.Add(btr);
            //        tr.AddNewlyCreatedDBObject(btr, true);

            //    }
            //    tr.Commit();
            //}

            AddBlock(layerName);
            //Application.SetSystemVariable("OSMODE", osm); //还原捕捉设置

            //选择基点
            Point3d pt = new Point3d();
            PromptPointOptions ppo = new PromptPointOptions("")
            {
                Message = "\n拾取基点",
                AllowNone = false
            };
            PromptPointResult ppr = ed.GetPoint(ppo);
            if (ppr.Status == PromptStatus.OK)
            {
                pt = ppr.Value;
            }
            else
            {
                return baseElv;
            }

            //输入基点标高

            PromptDoubleOptions pdo = new PromptDoubleOptions("")
            {
                Message = "\n请输入基点标高",
                AllowNone = false,
                DefaultValue = 100
            };
            PromptDoubleResult pdr = ed.GetDouble(pdo);
            if (pdr.Status != PromptStatus.OK)
            {
                return baseElv;
            }

            baseElv = pdr.Value;

            return pt.Y - baseElv;
        }
        /// <summary>
        /// 定义带填充的块，事务相互嵌套，需要慢慢体会
        /// </summary>
        /// <param name="layerName"></param>
        /// <returns></returns>
        public static ObjectIdCollection AddBlock(string layerName)
        {
            //获取文档和数据库
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            ObjectIdCollection ids = new ObjectIdCollection();
            AddLayer(layerName);
            //开启一个事务
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                //以读的方式打开块表
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                ObjectId lId = ObjectId.Null;

                //判断标高块是否存在
                if (!bt.Has(layerName))
                {
                    //新建块
                    using (OpenCloseTransaction tr1 = db.TransactionManager.StartOpenCloseTransaction())
                    {
                        using (BlockTableRecord btr = new BlockTableRecord())
                        {
                            btr.Name = layerName;
                            btr.Origin = new Point3d(0, 0, 0);
                            //创建闭合多段线，作为填充的边界
                            using (Polyline acPoly = new Polyline())
                            {
                                acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
                                acPoly.AddVertexAt(1, new Point2d(2, 2), 0, 0, 0);
                                acPoly.AddVertexAt(2, new Point2d(-2, 2), 0, 0, 0);
                                acPoly.Closed = true;
                                acPoly.Layer = layerName;
                                //将多段线添加到块表记录和事务
                                btr.AppendEntity(acPoly);

                                //将多段线ID添加到ID集合

                                tr.GetObject(db.BlockTableId, OpenMode.ForWrite);
                                bt.Add(btr);
                                tr.AddNewlyCreatedDBObject(btr, true);
                                lId = acPoly.ObjectId;
                            }
                        }

                        ids.Add(lId);
                        //将对象保存至数据库
                        tr1.Commit();
                    }
                    //必须将pl绘制的事务提交，才能调用其ObjectID
                    using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                    {
                        BlockTable bt1 = tr2.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                        BlockTableRecord btr = tr2.GetObject(bt1[layerName], OpenMode.ForWrite) as BlockTableRecord;
                        //创建填充并添加至块表记录
                        using (Hatch acHatch = new Hatch())
                        {
                            btr.AppendEntity(acHatch);
                            //设置填充属性
                            //关联必须在添加到块表记录之后，设置边界之前设置
                            acHatch.SetHatchPattern(HatchPatternType.PreDefined, "_SOLID");
                            acHatch.Associative = true;
                            acHatch.AppendLoop(HatchLoopTypes.Outermost, ids);
                            acHatch.Layer = layerName;
                            acHatch.EvaluateHatch(true);
                            ids.Add(acHatch.ObjectId);
                        }
                        tr2.Commit();
                    }
                }
                tr.Commit();
            }

            return ids;
        }

        /// <summary>
        /// 逐个标注直线坡度
        /// </summary>
        /// <param name="jd"></param>
        /// <param name="tHeight"></param>
        internal static void GetSlope(int jd, double tHeight)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            do
            {
                //选择直线线对象
                PromptEntityOptions peo = new PromptEntityOptions("\n选择要标注坐标的直线");
                peo.SetRejectMessage("\n只能选择直线（Line）");
                peo.AddAllowedClass(typeof(Line), false);
                PromptEntityResult per = ed.GetEntity(peo);
                if (per.Status != PromptStatus.OK)
                {
                    return;
                }

                Line line = new Line();
                using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
                {
                    line = tr.GetObject(per.ObjectId, OpenMode.ForWrite) as Line;
                    line.GetSlopeBase(jd, tHeight);
                    tr.Commit();
                }

            } while (true);
        }

        internal static void GetPolylineSlpoe(int jd, double tHeight)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            Point3d[] pts = new Point3d[2];

            //选择多段线对象，并获取点的集合
            PromptEntityOptions peo = new PromptEntityOptions("\n选择要标注坐标的多段线");
            peo.SetRejectMessage("\n只能选择多段线（Polyline）");
            peo.AddAllowedClass(typeof(Polyline), false);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
            {
                return;
            }

            Polyline pl = new Polyline();
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                pl = tr.GetObject(per.ObjectId, OpenMode.ForWrite) as Polyline;
                if (pl != null)
                {
                    if (pl.GetPoint3dAt(0).X > pl.GetPoint3dAt(1).X)
                    {
                        pl.ReverseCurve();
                    }

                    int vn = pl.NumberOfVertices;
                    for (int i = 0; i < vn - 1; i++)
                    {

                        using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                        {
                            pts[0] = pl.GetPoint3dAt(i);
                            pts[1] = pl.GetPoint3dAt(i + 1);
                            Line line = new Line(pts[0], pts[1]);
                            line.GetSlopeBase(jd, tHeight);
                            tr2.Commit();
                        }
                    }
                }

                tr.Commit();
            }
        }

        public static void GetSlopeBase(this Line line, int jd, double tHeight)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            string layerName = "ZC-LJ-PD";
            AddLayer(layerName);
            using (OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction())
            {
                line.ReverseLine();
                double pd = Math.Abs(Math.Tan(line.Angle));
                DBText text = new DBText
                {
                    Height = tHeight,
                    HorizontalMode = TextHorizontalMode.TextMid,
                    VerticalMode = TextVerticalMode.TextBottom
                };
                Point3d textPt = PolarPoints(MidPoints(line), line.Angle + Math.PI / 2, tHeight * 1.2);
                text.AlignmentPoint = textPt;
                text.Rotation = line.Angle;
                text.Layer = layerName;
                if (pd < 0.1)
                {
                    string jd1 = "p" + jd;
                    string s = pd.ToString(jd1);
                    text.TextString = s.Replace(" ", "");
                    AddSlopeBlock(layerName);
                    //插入图块
                    double sc = tHeight * (1 + (jd - 2) / 5.0);//块比例随精度增加，注意整除
                    Point3d blockPt = PolarPoints(MidPoints(line), line.Angle + Math.PI / 2, tHeight / 3);
                    Scale3d scale3D = new Scale3d(sc, sc, sc); //缩放比例
                    ObjectId blockId = db.InsertBlockTableRecord(layerName, layerName, blockPt, scale3D, line.Angle);
                    db.AddEntity(text);
                    if (line.Angle < Math.PI)
                    {//对箭头方向进行镜像，使其始终指向低点
                        Line3d minLine = new Line3d(MidPoints(line), textPt);//镜像的基线应为Line3D
                        using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                        {
                            BlockReference blockReference = tr2.GetObject(blockId, OpenMode.ForWrite) as BlockReference; //此处是块引用
                            blockReference.TransformBy(Matrix3d.Mirroring(minLine));//镜像操作
                            tr2.Commit();
                        }
                    }
                }
                else
                {
                    //标注坡比
                    string jd1 = "f" + jd;
                    string s = (1 / pd).ToString(jd1);
                    text.TextString = $"1:{s}";
                    db.AddEntity(text);
                }
                tr.Commit();
            }
        }

        /// <summary>
        /// 定义带填充的块，事务相互嵌套，需要慢慢体会
        /// </summary>
        /// <param name="layerName"></param>
        /// <returns></returns>
        public static ObjectIdCollection AddSlopeBlock(string layerName)
        {
            //获取文档和数据库
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            ObjectIdCollection ids = new ObjectIdCollection();
            AddLayer(layerName);
            //开启一个事务
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                //以读的方式打开块表
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                ObjectId lId = ObjectId.Null;

                //判断标高块是否存在
                if (!bt.Has(layerName))
                {
                    //新建块
                    using (OpenCloseTransaction tr1 = db.TransactionManager.StartOpenCloseTransaction())
                    {
                        using (BlockTableRecord btr = new BlockTableRecord())
                        {
                            btr.Name = layerName;
                            btr.Origin = new Point3d(0, 0, 0);
                            //创建闭合多段线，作为填充的边界
                            using (Polyline acPoly = new Polyline())
                            {
                                acPoly.AddVertexAt(0, new Point2d(-2, 0), 0, 0, 0);
                                acPoly.AddVertexAt(1, new Point2d(2, 0), 0, 0, 0);
                                acPoly.AddVertexAt(2, new Point2d(3.6, 0), 0, 0, 0);
                                acPoly.AddVertexAt(3, new Point2d(2, 0.3), 0, 0, 0);
                                acPoly.AddVertexAt(4, new Point2d(2, 00), 0, 0, 0);
                                acPoly.Closed = true;
                                acPoly.Layer = layerName;
                                //将多段线添加到块表记录和事务
                                btr.AppendEntity(acPoly);

                                //将多段线ID添加到ID集合

                                tr.GetObject(db.BlockTableId, OpenMode.ForWrite);
                                bt.Add(btr);
                                tr.AddNewlyCreatedDBObject(btr, true);
                                lId = acPoly.ObjectId;
                            }
                        }

                        ids.Add(lId);
                        //将对象保存至数据库
                        tr1.Commit();
                    }
                    //必须将pl绘制的事务提交，才能调用其ObjectID
                    using (OpenCloseTransaction tr2 = db.TransactionManager.StartOpenCloseTransaction())
                    {
                        BlockTable bt1 = tr2.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                        BlockTableRecord btr = tr2.GetObject(bt1[layerName], OpenMode.ForWrite) as BlockTableRecord;
                        //创建填充并添加至块表记录
                        using (Hatch acHatch = new Hatch())
                        {
                            btr.AppendEntity(acHatch);
                            //设置填充属性
                            //关联必须在添加到块表记录之后，设置边界之前设置
                            acHatch.SetHatchPattern(HatchPatternType.PreDefined, "_SOLID");
                            acHatch.Associative = true;
                            acHatch.AppendLoop(HatchLoopTypes.Outermost, ids);
                            acHatch.Layer = layerName;
                            acHatch.EvaluateHatch(true);
                            ids.Add(acHatch.ObjectId);
                        }
                        tr2.Commit();
                    }
                }
                tr.Commit();
            }

            return ids;
        }
        /// <summary>
        /// 将道岔部分从线路图中拆分出来，方便后期生成模型
        /// - 按顺序选择主线及岔线
        /// - 对直线方向进行判断，若为反向，进行翻转
        /// - 判断岔心位置
        /// - 根据两条直线及岔心位置，生成道岔，并对原线路直线进行修改
        /// </summary>
        public static void DaoCha()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor editor = doc.Editor;
            Database db = doc.Database;
            while (true)
            {
                ObjectId id1 = SelectEntity("选择主线");
                if (id1 == ObjectId.Null)
                {
                    break;
                }
                ObjectId id2 = SelectEntity("选择岔线");
                if (id2 == ObjectId.Null)
                {
                    break;
                }
                using (Transaction trans = db.TransactionManager.StartOpenCloseTransaction())
                {
                    BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                    BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                    Line line1 = trans.GetObject(id1, OpenMode.ForWrite) as Line;
                    Line line2 = trans.GetObject(id2, OpenMode.ForWrite) as Line;
                    if (line2.EndPoint.DisToLine(line1) <= 0.2)
                    {
                        line2.ReverseCurve();
                    }

                    if (Math.Cos(line1.Angle - line2.Angle) < 0)
                    {
                        line1.ReverseCurve();
                    }
                    Point3d jd = line2.StartPoint;
                    Line line3 = new Line();
                    Line line4 = new Line();
                    line3.StartPoint = jd;
                    line3.EndPoint = PolarPoints(jd, line2.Angle, 18.833);
                    line4.StartPoint = PolarPoints(jd, line1.Angle, -11.194);
                    line4.EndPoint = PolarPoints(jd, line1.Angle, 18.833);
                    Line line5 = line1.Clone() as Line;
                    line5.StartPoint = line4.EndPoint;
                    db.AddEntity(line5);
                    string layerName = "ZC-XL-DC";
                    AddLayer(layerName, 10);
                    List<Line> lines = new List<Line> {
                        line3,
                        line4,
                    };
                    foreach (Line line in lines)
                    {
                        line.Layer = layerName;
                        db.AddEntity(line);
                    }
                    line1.EndPoint = line4.StartPoint;
                    line2.StartPoint = line3.EndPoint;

                    trans.Commit();
                }
            }

        }
        /// <summary>
        /// 选择对象
        /// </summary>
        /// <param name="infomation">提示信息</param>
        /// <returns>返回对象ID</returns>
        public static ObjectId SelectEntity(string infomation)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor editor = doc.Editor;
            ObjectId id = ObjectId.Null;
            PromptEntityResult re = editor.GetEntity(infomation);
            if (re.Status == PromptStatus.OK)
            {
                id = re.ObjectId;
            }
            return id;
        }
        /// <summary>
        /// 获取点到直线的距离
        /// </summary>
        /// <param name="pt">点</param>
        /// <param name="line">直线</param>
        /// <returns>点到直线的距离</returns>
        public static double DisToLine(this Point3d pt, Line line)
        {
            Point3d pt1 = line.StartPoint;
            Point3d pt2 = line.EndPoint;
            if (pt1.Y == pt2.Y)
            {
                return Math.Abs(pt.Y - pt2.Y);
            }
            double k = (pt1.X - pt2.X) / (pt1.Y - pt2.Y);
            double c = (pt1.Y * pt2.X - pt2.Y * pt1.X) / (pt1.Y - pt2.Y);
            return Math.Abs(k * pt.Y - pt.X + c) / Math.Sqrt(k * k + 1);
        }
        /// <summary>
        /// 批量选择文本，根据特定符号对文本进行分割，将文字修改为特定符号之前的文本
        /// </summary>
        public static void GetText1(string c,bool? check)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                SelectionFilter filter = new SelectionFilter(new[] { new TypedValue((int)DxfCode.Start, "TEXT") });
                try
                {
                    PromptSelectionOptions pso = new PromptSelectionOptions
                    {
                        MessageForAdding = "\n请选择文本(ESC退出)",
                        SingleOnly = false
                    };
                    PromptSelectionResult pr = ed.GetSelection(pso, filter);
                    if (pr.Status == PromptStatus.OK)
                    {
                        ObjectId[] ids = pr.Value.GetObjectIds();
                        DBText[] dbTexts = new DBText[ids.Length];
                        string[] strs = new string[ids.Length];
                        for (int i = 0; i < ids.Length; i++)
                        {
                            dbTexts[i] = tr.GetObject(ids[i], OpenMode.ForWrite) as DBText;
                            if (dbTexts[i].TextString.Contains(c))
                            {
                                if (check==true)
                                {
                                    strs[i] = dbTexts[i].TextString.Split(new string[] {c}, StringSplitOptions.RemoveEmptyEntries)[0];
                                }
                                else
                                {
                                    strs[i] = dbTexts[i].TextString.Split(new string[] { c }, StringSplitOptions.RemoveEmptyEntries)[1];
                                }
                                dbTexts[i].TextString = strs[i];
                            }
                        }

                    }
                    else
                    {
                        return;
                    }


                }
                catch (Autodesk.AutoCAD.Runtime.Exception e)
                {
                    ed.WriteMessage(e.Message);
                }

                tr.Commit();
            }

        }
    }
}


